package fr.jade.reflex.util;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Fractal;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.factory.GenericFactory;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;

import fr.jade.fraclite.factory.ChainedInstantiationException;
import fr.jade.reflex.api.control.BindingNotificationController;
import fr.jade.reflex.api.control.ContentNotificationController;
import fr.jade.reflex.api.control.GenericAttributeNotificationController;
import fr.jade.reflex.api.control.LifeCycleNotificationController;
import fr.jade.reflex.api.control.NameNotificationController;
import fr.jade.reflex.api.control.ReflexController;
import fr.jade.reflex.api.factory.GenericFactoryNotification;
import fr.jade.util.log.Logger;

/**
 * Provides utility methods to deal with reflexive components.
 * 
 * @author Fabienne Boyer
 * @contact Fabienne.Boyer@inrialpes.fr
 * 
 */

public class Reflex {

    // prefix for controllers names in Julia configuration file
//    public static String execControllersPrefix = "reflex-exec-";
//    public static String metaControllersPrefix = "reflex-meta-";
    
//    public static String getControllersPrefix(String level){
//      return level.equals(ReflexController.EXEC_LEVEL)?execControllersPrefix:metaControllersPrefix;
//    }

    /**
     * Returns the {@link ReflexController} interface of the given component.
     * 
     * @param component
     *            a component.
     * @return the {@link ReflexController} interface of�the given component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static ReflexController getReflexController(final Component component) {
      ReflexController rc = null;
      try {
        rc = getOptReflexController(component);
      } catch (NoSuchInterfaceException e) { }
      assert rc != null : "Component must have a reflex-controller";
      return rc;   
    }

    public static ReflexController getOptReflexController(final Component component) throws NoSuchInterfaceException {
      return (ReflexController) component.getFcInterface("reflex-controller");
    }
    /**
     * Returns the {@link BindingNotificationController} interface of the given
     * component.
     * 
     * @param component
     *            a component.
     * @return the {@link BindingNotificationController} interface of�the given
     *         component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static BindingNotificationController getBindingNotificationController(
            final Component component) throws NoSuchInterfaceException {
        return (BindingNotificationController) component
                .getFcInterface("binding-controller");
    }

    /**
     * Returns the {@link ContentNotificationController} interface of the given
     * component.
     * 
     * @param component
     *            a component.
     * @return the {@link ContentNotificationController} interface of�the given
     *         component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static ContentNotificationController getContentNotificationController(
            final Component component) throws NoSuchInterfaceException {
        return (ContentNotificationController) component
                .getFcInterface("content-controller");
    }

    /**
     * Returns the {@link LifeCycleNotificationController} interface of the
     * given component.
     * 
     * @param component
     *            a component.
     * @return the {@link LifeCycleNotificationController} interface of the
     *         given component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static LifeCycleNotificationController getLifeCycleNotificationController(
            final Component component) throws NoSuchInterfaceException {
        return (LifeCycleNotificationController) component
                .getFcInterface("lifecycle-controller");
    }

    /**
     * Returns the {@link NameNotificationController} interface of the given
     * component.
     * 
     * @param component
     *            a component.
     * @return the {@link NameNotificationController} interface of�the given
     *         component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static NameNotificationController getNameNotificationController(
            final Component component) throws NoSuchInterfaceException {
        return (NameNotificationController) component
                .getFcInterface("name-controller");
    }

    /**
     * Returns the {@link GenericAttributeNotificationController} interface of
     * the given component.
     * 
     * @param component
     *            a component.
     * @return the {@link GenericAttributeNotificationController} interface
     *         of�the given component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static GenericAttributeNotificationController getGenericAttributeNotificationController(
            final Component component) throws NoSuchInterfaceException {
        return (GenericAttributeNotificationController) component
                .getFcInterface("generic-attribute-controller");
    }

    /**
     * Returns the {@link GenericFactoryNotification} interface of the given
     * component.
     * 
     * @param component
     *            a component.
     * @return the {@link GenericFactoryNotification} interface of�the given
     *         component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static GenericFactoryNotification getGenericFactoryNotification(
            final Component component) throws NoSuchInterfaceException {
        return (GenericFactoryNotification) component
                .getFcInterface("generic-factory");
    }

    /**
     * Returns the {@link Component} interface of the given component.
     * 
     * @param component
     *            a component.
     * @return the {@link Component} interface of�the given component.
     * @throws NoSuchInterfaceException
     *             if there is no such interface.
     */

    public static Component getComponent(final Component component)
            throws NoSuchInterfaceException {
        return (Component) component.getFcInterface("component");
    }

    /**
     * Get the dual level of a given level
     * 
     * @param l
     *            the level
     * @return ReflexController.EXEC_LEVEL if the level is equal to
     *         eflexController.META_LEVEL ReflexController.META_LEVEL if the
     *         level is equal to eflexController.EXEC_LEVEL
     *         ReflexController.NO_LEVEL if the level is error-prone
     */

    public static String getDualLevel(String l) {
        if (l.equals(ReflexController.EXEC_LEVEL))
            return ReflexController.META_LEVEL;
        else if (l.equals(ReflexController.META_LEVEL))
            return ReflexController.EXEC_LEVEL;
        else
            return ReflexController.NO_LEVEL;
    }

    /**
     * Get the dual componant of a given component
     * 
     * @param o
     *            the component reference
     * @return the dual componant reference null if the conversion is impossible
     */

    public static Component getOptDualComponent(Component c) throws NoSuchInterfaceException {
        return ((ReflexController) c.getFcInterface("reflex-controller")).getCmpRef();
    }

    /**
     * Get the dual componant of a given component
     * 
     * @param o
     *            the component reference
     * @return the dual componant reference ; AssertionError is thrown if the conversion is impossible
     */

    public static Component getDualComponent(Component c) {
        Component dual = null;
        try{
          dual = getOptDualComponent(c);
        }catch(NoSuchInterfaceException ignored){}
        assert dual != null : "No dual component";
        return dual;
    }
    
    /**
     * Get the dual controller of a given controller
     * 
     * @param comp
     *            the controller's component
     * @param name
     *            the controller's interface name
     * @return the dual controller reference null if the conversion is
     *         impossible
     */

    public static Interface getOptDualController(Component comp, String name) throws NoSuchInterfaceException {
        Component dualcomp = getOptDualComponent(comp);
        return (Interface) (dualcomp.getFcInterface(name));
    }

    /**
     * Get the dual controller of a given controller
     * 
     * @param comp 
     *            the controller's component
     * @param name
     *            the controller's interface name
     * @return the dual controller reference ; AssertionError is thrown if the conversion is
     *         impossible
     */

    public static Interface getDualController(Component comp, String name) {
      assert name != null;
      Component dualcomp = getDualComponent(comp);
      Interface itf = null;
      try {
        itf = (Interface) (dualcomp.getFcInterface(name)); 
      } catch (NoSuchInterfaceException ignored) { }
      assert itf != null : "Dual component don't have " + name + " interface";
      return itf;
    }
    
    /**
     * Get the dual interface of a given interface
     * 
     * @param i
     *            the interface reference
     * @return the dual interface reference null if the conversion is impossible
     */

    public static Interface getOptDualInterface(Interface itf) throws NoSuchInterfaceException {

        String itfname = ((Interface) itf).getFcItfName();
        Component dualcomp = getOptDualComponent(((Interface) itf).getFcItfOwner());

        if (((Interface) itf).isFcInternalItf()) {
            ContentController dualcc = (ContentController) (dualcomp.getFcInterface("content-controller"));
            return (Interface) (dualcc.getFcInternalInterface(itfname));
        } else {
            return (Interface) (dualcomp.getFcInterface(itfname));
        }
    }

    public static Interface getDualInterface(Interface itf) {
      Interface i = null;
      try {
        i = getOptDualInterface(itf);
      } catch (NoSuchInterfaceException ignored) { }
      assert i != null : "Interface must have a dual";
      return i;
    }
    
    /**
     * Convert the reference
     * 
     * @param o
     *            the ref to be converted
     * @return the ref converted if necessary null if the conversion is
     *         impossible
     */

    public static Object getOptDual(Object o) throws NoSuchInterfaceException {

        if (o instanceof Interface) {
            return getOptDualInterface((Interface) o);

        } else if (o instanceof Component) {
            return getOptDualComponent((Component) o);

        } else
            // nothing2do
            Logger.println("Reflex.getDual : error");
        return o;
    }

    /**
     * return the level of the given component (meta / exec)
     * 
     * @param o
     *            the ref of the component
     * @return the level of the given component
     */
    public static String getLevel(Interface itf) {
        Component c = itf.getFcItfOwner();
        return getLevel(c);
    }

    public static String getLevel(Component c) {
      ReflexController rc = getReflexController(c);
      return rc.getLevel();
    }
    
    /**
     * Test if the reference passed as parameter belongs to a given level.
     * 
     * @param o
     *            the reference to be tested
     * @param l
     *            the level
     * @return true if the reference belongs to the given leve modified 16/05/05
     *         F. Boyer : use getLevel method
     */
    protected static boolean isLevel(Interface itf, String l) {
        return getLevel(itf).equals(l);
    }

    /**
     * Test if the reference passed as parameter is a reference at the exec
     * level.
     * 
     * @param o
     *            the ref to be tested
     * @return true if it is a ref to the exec level modified 16/05/05 F. Boyer :
     *         use isLevel method
     */
    public static boolean isEL(Interface itf) {
        return isLevel(itf, ReflexController.EXEC_LEVEL);
    }

    public static boolean isEL(Component itf) {
      return isLevel((Interface)itf, ReflexController.EXEC_LEVEL);
    }
    /**
     * Test if the reference passed as parameter is a reference at the meta
     * level.
     * 
     * @param o
     *            the ref to be tested
     * @return true if it is a ref to the meta level modified 16/05/05 F. Boyer :
     *         use isLevel method
     */
    public static boolean isML(Interface itf) {
        return isLevel(itf, ReflexController.META_LEVEL);
    }
    
    public static boolean isML(Component itf) {
      return isLevel((Interface)itf, ReflexController.META_LEVEL);
    }
    
    public static void setAsDualComponent(Component cmp, Component dual) throws NoSuchInterfaceException {
      getReflexController(cmp).setCmpRef(dual);
      getReflexController(dual).setCmpRef(cmp);
    }
}
